home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Tools / Languages / MacMETH 3.2.1 / Sources / MacC2.6 / M2SM.MOD < prev    next >
Encoding:
Modula Implementation  |  1992-05-29  |  15.5 KB  |  499 lines  |  [TEXT/MEDT]

  1. IMPLEMENTATION MODULE M2SM; (* NW 17.8.83 / 23.3.85; HS 31.5.86 / 30.4.91 *)
  2.  
  3.   FROM SYSTEM IMPORT LONG, SHORT, VAL;
  4.   FROM FileSystem IMPORT
  5.     File, Response, Lookup, ReadChar, WriteChar, Close;
  6.   FROM FileUtil IMPORT GetPos, SetPos;
  7.   FROM M2DM IMPORT MaxCard, rngchk, ovflchk;
  8.  
  9.   CONST KW = 43; (* number of keywords *)
  10.         maxDig = 15; (* to avoid floating overflow in scanner *)
  11.         maxExp = 308;(* note the correspondence with the pow-array! *)
  12.         maxBuf = 30; (* maximum length for a number *)
  13.         IdBufLim = IdBufLeng - 100;
  14.         FNM = 300C;
  15.         ERR = 301C;
  16.         MaxOptionLevel = 10;
  17.  
  18.   VAR ch:     CHAR;      (* current character *)
  19.     id0,id1:  INTEGER;   (* indices of identifier buffer *)
  20.     pow:      ARRAY [0..8] OF LONGREAL;(* must cover exponent range *)
  21.     keyTab:   ARRAY [0..KW-1] OF
  22.                 RECORD sym: Symbol; ind: INTEGER END;
  23.     K:        CARDINAL;
  24.     rOptions, vOptions: ARRAY[0..MaxOptionLevel] OF BOOLEAN;
  25.     rIndex,vIndex: INTEGER;
  26.     lastPos:  LONGINT;
  27.     errLog,
  28.     errDat:   File;
  29.  
  30.   PROCEDURE ErrorBlock(errPos: LONGINT; errCod: CARDINAL);
  31.     VAR i: CARDINAL;
  32.         conv: RECORD
  33.                  CASE :CARDINAL OF
  34.                   0: long: LONGINT
  35.                 | 1: sys:  ARRAY [0..3] OF CHAR;
  36.                 END;
  37.               END;
  38.   BEGIN
  39.     IF errPos >= 2D THEN errPos := errPos - 2D END;
  40.     WriteChar(errDat, ERR);
  41.     WITH conv DO
  42.       long := errPos; FOR i := 3 TO 0 BY -1 DO WriteChar(errDat, sys[i]) END;
  43.     END;
  44.     WriteChar(errDat,CHR(errCod MOD 256)); WriteChar(errDat,CHR(errCod DIV 256));
  45.   END ErrorBlock;
  46.  
  47.   PROCEDURE Mark(n: INTEGER);
  48.     VAR k: CARDINAL; buf: CHAR; pos: LONGINT;
  49.       dig: ARRAY [0..3] OF CARDINAL;
  50.   BEGIN scanerr := TRUE; k := 4; GetPos(source, pos);
  51.     IF lastPos + 8D < pos THEN
  52.       ErrorBlock(pos, n);
  53.       IF lastPos + 100D < pos THEN
  54.         lastPos := pos - 100D; SetPos(source, lastPos);
  55.         WriteChar(errLog, 15C); WriteChar(errLog, 15C);
  56.         REPEAT ReadChar(source, buf); INC(lastPos)
  57.         UNTIL (buf = 15C) OR (lastPos = pos)
  58.       ELSE SetPos(source, lastPos)
  59.       END;
  60.       WHILE lastPos < pos DO
  61.         ReadChar(source, buf); WriteChar(errLog, buf); INC(lastPos);
  62.       END;
  63.       WriteChar(errLog, " ");
  64.       REPEAT WriteChar(errLog, "*"); DEC(k) UNTIL k = 0;
  65.       REPEAT dig[k] := n MOD 10; n := n DIV 10; INC(k) UNTIL n = 0;
  66.       REPEAT DEC(k); WriteChar(errLog, CHR(dig[k] + 60B)) UNTIL k = 0;
  67.       WriteChar(errLog, " ")
  68.     END;
  69.   END Mark;
  70.  
  71.   PROCEDURE GetCh;
  72.   BEGIN ReadChar(source, ch)
  73.   END GetCh;
  74.  
  75.   (*$R- to speed up and to avoid range errors for invalid i or j *)
  76.  
  77.   PROCEDURE Diff(i, j: INTEGER): INTEGER;
  78.     VAR k: CARDINAL; d: INTEGER;
  79.   BEGIN
  80.     IF IdBuf[i] # IdBuf[j] THEN
  81.       RETURN VAL(INTEGER,ORD(IdBuf[i])) - VAL(INTEGER,ORD(IdBuf[j]))
  82.     END;
  83.     k := ORD(IdBuf[i])-1; INC(i); INC(j);
  84.     LOOP
  85.       IF k = 0 THEN RETURN 0
  86.       ELSIF IdBuf[i] # IdBuf[j] THEN
  87.         RETURN VAL(INTEGER,ORD(IdBuf[i])) - VAL(INTEGER,ORD(IdBuf[j]))
  88.       ELSE INC(i); INC(j); DEC(k)
  89.       END
  90.     END
  91.   END Diff;
  92.  
  93.   PROCEDURE KeepId;
  94.   BEGIN id := id1
  95.   END KeepId;
  96.  
  97.   PROCEDURE String(termCh: CHAR);
  98.   BEGIN id1 := id + 1;
  99.     IF id1 > IdBufLim THEN Mark(91); id1 := 1 END;
  100.     LOOP ReadChar(source, ch);
  101.       IF ch = termCh THEN EXIT END;
  102.       IF ch < " " THEN Mark(45); EXIT END;
  103.       IdBuf[id1] := ch; INC(id1)
  104.     END;
  105.     ReadChar(source, ch);
  106.     IF id1-id <= ORD(MAX(CHAR)) THEN
  107.       IdBuf[id] := CHR(id1-id); (*length*)
  108.     ELSE
  109.       IdBuf[id] := MAX(CHAR);  (*default maximum length*)
  110.       Mark(146);
  111.     END;
  112.     IF IdBuf[id] = 2C THEN
  113.       sym := number; numtyp := 3; intval := ORD(IdBuf[id+1])
  114.     ELSE sym := string;
  115.       IF IdBuf[id] = 1C THEN  (*empty string*)
  116.         IdBuf[id1] := 0C; INC(id1); IdBuf[id] := 2C
  117.       END
  118.     END
  119.   END String;
  120.  
  121.   PROCEDURE Identifier;
  122.     VAR k, l, m: CARDINAL; cap: BOOLEAN;
  123.   BEGIN id1 := id + 1; cap := TRUE;
  124.     IF id1 > IdBufLim THEN Mark(91); id1 := 1 END;
  125.     REPEAT
  126.       IdBuf[id1] := ch;
  127.       cap := cap AND (ch <= "Z");
  128.       INC(id1); ReadChar(source, ch)
  129.     UNTIL ((CAP(ch) < "A") OR ("Z" < CAP(ch))) & ((ch < "0") OR ("9" < ch));
  130.     IdBuf[id] := CHR(id1-id); (*Length*)
  131.     IF cap THEN
  132.       k := 0; l := KW;
  133.       REPEAT m := (k + l) DIV 2;
  134.         IF Diff(id, keyTab[m].ind) <= 0 THEN l := m ELSE k := m + 1 END
  135.       UNTIL k >= l;
  136.       IF (k < KW) & (Diff(id, keyTab[k].ind) = 0) THEN sym := keyTab[k].sym
  137.       ELSE sym := ident
  138.       END
  139.     ELSE sym := ident
  140.     END
  141.   END Identifier;
  142.  
  143.   PROCEDURE Number;
  144.     VAR i, j, l, d, e, n: CARDINAL;
  145.     x, f:   LONGREAL;
  146.     d0, d1: LONGINT;
  147.     neg:    BOOLEAN;
  148.     lastCh: CHAR;
  149.     dig:    ARRAY [0..maxBuf] OF CHAR;
  150.  
  151.     PROCEDURE Ten(e: CARDINAL): LONGREAL;
  152.         VAR k: CARDINAL; u: LONGREAL;
  153.     BEGIN k := 0; u := FLOATD(1);
  154.       WHILE e > 0 DO
  155.         IF ODD(e) THEN u := pow[k] * u END;
  156.         e := e DIV 2; INC(k)
  157.       END;
  158.       RETURN u
  159.     END Ten;
  160.  
  161.   BEGIN sym := number; i := 0; l := 0;
  162.     REPEAT dig[i] := ch; INC(l);
  163.       IF i < maxBuf THEN INC(i) END;
  164.       ReadChar(source, ch)
  165.     UNTIL (ch < "0") OR ("9" < ch) & (CAP(ch) < "A") OR ("Z" < CAP(ch));
  166.     IF l > maxBuf THEN Mark(46) END; (* too many digits *)
  167.     lastCh := ch; j := 0;
  168.     WHILE (j < i) & (dig[j] = "0") DO INC(j) END;
  169.     IF ch = "." THEN ReadChar(source, ch);
  170.       IF ch = "." THEN
  171.         lastCh := 0C; ch := 177C (*ellipsis*)
  172.       END
  173.     END;
  174.     IF lastCh = "." THEN (*decimal point*)
  175.       x := FLOATD(0); l := 0;
  176.       WHILE j < i DO (*read integer part*)
  177.         IF l < maxDig THEN
  178.           IF dig[j] > "9" THEN Mark(40) END;
  179.           x := x * FLOATD(10) + FLOATD(ORD(dig[j]) - 60B);
  180.           INC(l)
  181.         ELSE Mark(41)
  182.         END;
  183.         INC(j)
  184.       END;
  185.       l := 0; f := FLOATD(0);
  186.       WHILE ("0" <= ch) & (ch <= "9") DO (*read fraction*)
  187.         IF l < maxDig THEN
  188.           f := f * FLOATD(10) + FLOATD(ORD(ch) - 60B);
  189.           INC(l)
  190.         END;
  191.         ReadChar(source, ch)
  192.       END;
  193.       x := f / Ten(l) + x; e := 0; neg := FALSE; numtyp := 4;
  194.       IF (ch = "E") OR (ch = "D") THEN
  195.         IF ch = "D" THEN numtyp := 5 END;
  196.         ReadChar(source, ch);
  197.         IF ch = "-" THEN
  198.           neg := TRUE; ReadChar(source, ch)
  199.         ELSIF ch = "+" THEN ReadChar(source, ch)
  200.         END;
  201.         WHILE ("0" <= ch) & (ch <= "9") DO (*read exponent*)
  202.           d := ORD(ch) - 60B;
  203.           IF (MaxCard - d) DIV 10 >= e THEN (* avoid cardinal ov *)
  204.             e := e*10 + d;
  205.           END;
  206.           ReadChar(source, ch)
  207.         END
  208.       END;
  209.       IF neg THEN
  210.         IF e <= maxExp THEN x := x / Ten(e) ELSE x := FLOATD(0) END
  211.       ELSE
  212.         IF e <= maxExp THEN f := Ten(e);
  213.           IF MAX(LONGREAL) / f >= x THEN x := f*x ELSE Mark(41) END
  214.         ELSE Mark(41)
  215.         END
  216.       END;
  217.       IF numtyp = 5 THEN lrlval := x
  218.       ELSIF x <= LONG(MAX(REAL)) THEN realval := SHORT(x)
  219.       ELSE Mark(41); realval := FLOAT(1)
  220.       END
  221.     ELSE (* integer *)
  222.       lastCh := dig[i-1];
  223.       IF lastCh = "B" THEN DEC(i);
  224.         numtyp := 1; intval := 0;
  225.         WHILE j < i DO
  226.           d := ORD(dig[j]) - 60B;
  227.           IF (d < 10B) & ((MaxCard - d) DIV 10B >= intval) THEN
  228.             intval := 10B * intval + d
  229.           ELSE Mark(29); intval := 0
  230.           END;
  231.           INC(j)
  232.         END
  233.       ELSIF lastCh = "H" THEN DEC(i);
  234.         IF i <= j+4 THEN
  235.           numtyp := 1; intval := 0;
  236.           WHILE j < i DO
  237.             d := ORD(dig[j]) - 60B;
  238.             IF d > 26B THEN Mark(29); d := 0
  239.                ELSIF d > 9 THEN d := d-7
  240.             END;
  241.             intval := 10H * intval + d; INC(j)
  242.           END
  243.         ELSIF i <= j+8 THEN
  244.           numtyp := 2; dblval := 0D;
  245.           REPEAT d := ORD(dig[j]) - 60B;
  246.             IF d > 26B THEN Mark(29); d := 0
  247.                ELSIF d > 9 THEN d := d-7
  248.             END;
  249.             dblval := dblval * 16D + LONG(d); INC(j)
  250.           UNTIL j = i
  251.         ELSE Mark(29); numtyp := 2; dblval := 0D
  252.         END
  253.       ELSIF lastCh = "D" THEN DEC(i);
  254.         numtyp := 2; d1 := 0D;
  255.         WHILE j < i DO
  256.           d := ORD(dig[j]) - 60B;
  257.           IF d < 10 THEN (*no overflow check*)
  258.             d1 := d1 + d1; d0 := d1 + d1; d1 := d0 + d0 + d1 + LONG(d)
  259.           ELSE Mark(29); d1 := 0D
  260.           END;
  261.           INC(j)
  262.         END;
  263.         dblval := d1
  264.       ELSIF lastCh = "C" THEN DEC(i);
  265.         numtyp := 3; intval := 0;
  266.         WHILE j < i DO
  267.           d := ORD(dig[j]) - 60B; intval := 10B * intval + d;
  268.           IF (d >= 10B) OR (intval >= 400B) THEN
  269.             Mark(29); intval := 0
  270.           END;
  271.           INC(j)
  272.         END
  273.       ELSE (* decimal *)
  274.         numtyp := 1; intval := 0;
  275.         WHILE j < i DO
  276.           d := ORD(dig[j]) - 60B;
  277.           IF (d < 10) & ((MaxCard - d) DIV 10 >= intval) THEN
  278.             intval := 10 * intval + d
  279.           ELSE Mark(29); intval := 0
  280.           END;
  281.           INC(j)
  282.         END
  283.       END
  284.     END
  285.   END Number;
  286.  
  287. (*$R=*)
  288.  
  289.   PROCEDURE GetSym;
  290.     VAR xch: CHAR;
  291.  
  292.     PROCEDURE TestOption;
  293.  
  294.       PROCEDURE MakeOption(VAR stack: ARRAY OF BOOLEAN; VAR index: INTEGER;
  295.                            VAR option: BOOLEAN);
  296.       BEGIN
  297.         ReadChar(source, ch);
  298.         IF    ch = "+" THEN
  299.           stack[index] := option; option := TRUE;
  300.           IF index < MaxOptionLevel THEN INC(index) END;
  301.         ELSIF ch = "-" THEN
  302.           stack[index] := option; option := FALSE;
  303.           IF index < MaxOptionLevel THEN INC(index) END;
  304.         ELSIF ch = "=" THEN
  305.           IF index > 0 THEN DEC(index) END;
  306.           option := stack[index]
  307.         ELSE
  308.           Mark(230)
  309.         END;
  310.       END MakeOption;
  311.  
  312.     BEGIN
  313.       IF    ch = "R" THEN MakeOption(rOptions, rIndex, rngchk)
  314.       ELSIF ch = "V" THEN MakeOption(vOptions, vIndex, ovflchk)
  315.       ELSE
  316.         Mark(230)  (* invalid option *)
  317.       END
  318.     END TestOption;
  319.  
  320.     PROCEDURE Comment;
  321.     BEGIN ReadChar(source, ch);
  322.       REPEAT
  323.         IF ch = "$" THEN ReadChar(source, ch); TestOption END;
  324.         WHILE (ch # "*") & (ch > 0C) DO
  325.           IF ch = "(" THEN ReadChar(source, ch);
  326.             IF ch = "*" THEN Comment END
  327.           ELSE ReadChar(source, ch)
  328.           END
  329.         END;
  330.         ReadChar(source, ch)
  331.       UNTIL (ch = ")") OR (ch = 0C);
  332.       IF ch > 0C THEN ReadChar(source, ch) ELSE Mark(42) END
  333.     END Comment;
  334.  
  335.   BEGIN
  336.     LOOP (*ignore control characters*)
  337.       IF ch <= " " THEN
  338.         IF ch = 0C THEN ch := " "; EXIT ELSE ReadChar(source, ch) END;
  339.       ELSIF ch > 177C THEN ReadChar(source, ch)
  340.       ELSE EXIT
  341.       END
  342.     END;
  343.     CASE ch OF   (* " " <= ch <= 177C *)
  344.         " "  : sym := eof; ch := 0C |
  345.         "!"  : sym := null; ReadChar(source, ch) |
  346.         '"'  : String('"') |
  347.         "#"  : sym := neq; ReadChar(source, ch)  |
  348.         "$"  : sym := null; ReadChar(source, ch) |
  349.         "%"  : sym := null; ReadChar(source, ch) |
  350.         "&"  : sym := and; ReadChar(source, ch)  |
  351.         "'"  : String("'") |
  352.         "("  : ReadChar(source, ch);
  353.                IF ch = "*" THEN Comment; GetSym
  354.                  ELSE sym := lparen
  355.                END |
  356.         ")"  : sym := rparen; ReadChar(source, ch)|
  357.         "*"  : sym := times; ReadChar(source, ch) |
  358.         "+"  : sym := plus; ReadChar(source, ch)  |
  359.         ","  : sym := comma; ReadChar(source, ch) |
  360.         "-"  : sym := minus; ReadChar(source, ch) |
  361.         "."  : ReadChar(source, ch);
  362.                IF ch = "." THEN ReadChar(source, ch); sym := ellipsis
  363.                  ELSE sym := period
  364.                END |
  365.         "/"  : sym := slash; ReadChar(source, ch) |
  366.         "0".."9": Number |
  367.         ":"  : ReadChar(source, ch);
  368.                IF ch = "=" THEN ReadChar(source, ch); sym := becomes
  369.                  ELSE sym := colon
  370.                END |
  371.         ";"  : sym := semicolon; ReadChar(source, ch) |
  372.         "<"  : ReadChar(source, ch);
  373.                IF ch = "=" THEN ReadChar(source, ch); sym := leq
  374.                  ELSIF ch = ">" THEN ReadChar(source, ch); sym := neq
  375.                  ELSE sym := lss
  376.                END |
  377.         "="  : sym := eql; ReadChar(source, ch)   |
  378.         ">"  : ReadChar(source, ch);
  379.                IF ch = "=" THEN ReadChar(source, ch); sym := geq
  380.                  ELSE sym := gtr
  381.                END |
  382.         "?"  : sym := null; ReadChar(source, ch)  |
  383.         "@"  : sym := null; ReadChar(source, ch)  |
  384.         "A".."Z": Identifier       |
  385.         "["  : sym := lbrak; ReadChar(source, ch) |
  386.         "\"  : sym := null; ReadChar(source, ch)  |
  387.         "]"  : sym := rbrak; ReadChar(source, ch) |
  388.         "^"  : sym := arrow; ReadChar(source, ch) |
  389.         "_"  : sym := becomes; ReadChar(source, ch)  |
  390.         "`"  : sym := null; ReadChar(source, ch)  |
  391.         "a".."z": Identifier       |
  392.         "{"  : sym := lbrace; ReadChar(source, ch)|
  393.         "|"  : sym := bar; ReadChar(source, ch)   |
  394.         "}"  : sym := rbrace; ReadChar(source, ch)|
  395.         "~"  : sym := not; ReadChar(source, ch)   |
  396.         177C : sym := ellipsis; ReadChar(source, ch)
  397.     END
  398.   END GetSym;
  399.  
  400.   PROCEDURE Enter(name: ARRAY OF CHAR): INTEGER;
  401.     VAR j, l: INTEGER;
  402.   BEGIN l := HIGH(name) + 2; id1 := id;
  403.     IF id1+l < IdBufLeng THEN
  404.       IdBuf[id] := CHR(l); INC(id);
  405.       FOR j := 0 TO l-2 DO IdBuf[id] := name[j]; INC(id) END
  406.     END;
  407.     RETURN id1
  408.   END Enter;
  409.  
  410.   PROCEDURE InitScanner(filename: ARRAY OF CHAR);
  411.     VAR i: CARDINAL;
  412.   BEGIN ch := " "; scanerr := FALSE; lastPos := 0;
  413.     IF id0 = 0 THEN
  414.       id0 := id; Lookup(errLog, "err.LST", TRUE);
  415.       Lookup(errDat, "err.DAT", TRUE);
  416.     ELSE id := id0; WriteChar(errLog, "-"); WriteChar(errLog, 36C)
  417.     END;
  418.     WriteChar(errDat, FNM); i := 0;
  419.     WHILE (i <= VAL(CARDINAL,HIGH(filename))) & (filename[i] # 0C) DO
  420.       WriteChar(errDat, filename[i]); INC(i);
  421.     END;
  422.     WriteChar(errDat, 0C);
  423.     rIndex := 0; vIndex := 0
  424.   END InitScanner;
  425.  
  426.   PROCEDURE CloseScanner;
  427.   BEGIN Close(errLog); Close(errDat);
  428.   END CloseScanner;
  429.  
  430.   PROCEDURE EnterKW(sym: Symbol; name: ARRAY OF CHAR);
  431.   VAR l, L: CARDINAL;
  432.   BEGIN
  433.     keyTab[K].sym := sym;
  434.     keyTab[K].ind := id;
  435.     l := 0; L := HIGH(name);
  436.     IdBuf[id] := CHR(L+2); INC(id);
  437.     WHILE l <= L DO
  438.       IdBuf[id] := name[l];
  439.       INC(id); INC(l)
  440.     END;
  441.     INC(K)
  442.   END EnterKW;
  443.  
  444. BEGIN K := 0; IdBuf[0] := 1C; id := 1; id0 := 0;
  445.   (* assert maxExp < 512 for actual pow! *)
  446.   pow[0] := FLOATD(10)       (* 1.0E1 *);
  447.   pow[1] := pow[0] * pow[0]  (* 1.0E2 *);
  448.   pow[2] := pow[1] * pow[1]  (* 1.0E4 *);
  449.   pow[3] := pow[2] * pow[2]  (* 1.0E8 *);
  450.   pow[4] := pow[3] * pow[3]  (* 1.0E16 *);
  451.   pow[5] := pow[4] * pow[4]  (* 1.0E32 *);
  452.   pow[6] := pow[5] * pow[5]  (* 1.0E64 *);
  453.   pow[7] := pow[6] * pow[6]  (* 1.0E128 *);
  454.   pow[8] := pow[7] * pow[7]  (* 1.0E256 *);
  455.   EnterKW(by,"BY");
  456.   EnterKW(do,"DO");
  457.   EnterKW(if,"IF");
  458.   EnterKW(in,"IN");
  459.   EnterKW(of,"OF");
  460.   EnterKW(or,"OR");
  461.   EnterKW(to,"TO");
  462.   EnterKW(and,"AND");
  463.   EnterKW(div,"DIV");
  464.   EnterKW(end,"END");
  465.   EnterKW(for,"FOR");
  466.   EnterKW(mod,"MOD");
  467.   EnterKW(not,"NOT");
  468.   EnterKW(rem,"REM");
  469.   EnterKW(set,"SET");
  470.   EnterKW(var,"VAR");
  471.   EnterKW(case,"CASE");
  472.   EnterKW(code,"CODE");
  473.   EnterKW(else,"ELSE");
  474.   EnterKW(exit,"EXIT");
  475.   EnterKW(from,"FROM");
  476.   EnterKW(loop,"LOOP");
  477.   EnterKW(then,"THEN");
  478.   EnterKW(type,"TYPE");
  479.   EnterKW(with,"WITH");
  480.   EnterKW(array,"ARRAY");
  481.   EnterKW(begin,"BEGIN");
  482.   EnterKW(const,"CONST");
  483.   EnterKW(elsif,"ELSIF");
  484.   EnterKW(until,"UNTIL");
  485.   EnterKW(while,"WHILE");
  486.   EnterKW(export,"EXPORT");
  487.   EnterKW(import,"IMPORT");
  488.   EnterKW(module,"MODULE");
  489.   EnterKW(record,"RECORD");
  490.   EnterKW(repeat,"REPEAT");
  491.   EnterKW(return,"RETURN");
  492.   EnterKW(forward,"FORWARD");
  493.   EnterKW(pointer,"POINTER");
  494.   EnterKW(procedure,"PROCEDURE");
  495.   EnterKW(qualified,"QUALIFIED");
  496.   EnterKW(definition,"DEFINITION");
  497.   EnterKW(implementation,"IMPLEMENTATION");
  498. END M2SM. (* Copyright Departement Informatik, ETH Zuerich, Switzerland, 1992 *)
  499.